Mestre asynkron iterasjon i JavaScript ved å bruke 'for await...of'-løkken og egendefinerte asynkrone iterator-hjelpere. Forbedre strømbehandling og datahåndtering.
JavaScript Asynkron Iterator Hjelper: For Each - Strømbehandling Iterasjon
Asynkron programmering er en hjørnestein i moderne JavaScript-utvikling, og gjør det mulig for applikasjoner å håndtere tidkrevende operasjoner uten å blokkere hovedtråden. Asynkrone iteratorer, introdusert i ECMAScript 2018, gir en kraftig mekanisme for å behandle datastrømmer asynkront. Dette blogginnlegget dykker ned i konseptet med asynkrone iteratorer og demonstrerer hvordan man implementerer en asynkron 'for each'-hjelpefunksjon for å effektivisere strømbehandling.
Forståelse av Asynkrone Iteratorer
En asynkron iterator er et objekt som er i samsvar med AsyncIterator-grensesnittet. Den definerer en next()-metode som returnerer et promise, som resolverer til et objekt med to egenskaper:
value: Den neste verdien i sekvensen.done: En boolsk verdi som indikerer om iteratoren er fullført.
Asynkrone iteratorer brukes ofte til å konsumere data fra asynkrone kilder som nettverksstrømmer, filsystemer eller databaser. for await...of-løkken gir en praktisk syntaks for å iterere over asynkrone itererbare objekter.
Eksempel: Lese fra en fil asynkront
Tenk deg et scenario der du trenger å lese en stor fil linje for linje uten å blokkere hovedtråden. Du kan oppnå dette ved å bruke en asynkron iterator:
const fs = require('fs');
const readline = require('readline');
async function* readFileLines(filePath) {
const fileStream = fs.createReadStream(filePath);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
for await (const line of rl) {
yield line;
}
}
async function processFile(filePath) {
for await (const line of readFileLines(filePath)) {
console.log(`Linje: ${line}`);
}
}
// Eksempel på bruk
processFile('path/to/your/file.txt');
I dette eksempelet er readFileLines en asynkron generatorfunksjon som yielder hver linje i filen etter hvert som den leses. processFile-funksjonen itererer deretter over linjene ved hjelp av for await...of, og behandler hver linje asynkront.
Implementere en Asynkron 'For Each'-hjelper
Selv om for await...of-løkken er nyttig, kan den bli omstendelig når du trenger å utføre komplekse operasjoner på hvert element i strømmen. En asynkron 'for each'-hjelpefunksjon kan forenkle denne prosessen ved å innkapsle iterasjonslogikken.
Grunnleggende Implementering
Her er en grunnleggende implementering av en asynkron 'for each'-funksjon:
async function asyncForEach(iterable, callback) {
for await (const item of iterable) {
await callback(item);
}
}
Denne funksjonen tar en asynkron itererbar og en callback-funksjon som argumenter. Den itererer over den itererbare ved hjelp av for await...of og kaller callback-funksjonen for hvert element. Callback-funksjonen bør også være asynkron hvis du vil vente på at den fullføres før du går videre til neste element.
Eksempel: Behandle data fra et API
Anta at du henter data fra et API som returnerer en strøm av elementer. Du kan bruke den asynkrone 'for each'-hjelperen til å behandle hvert element etter hvert som det ankommer:
async function* fetchDataStream(url) {
const response = await fetch(url);
const reader = response.body.getReader();
const decoder = new TextDecoder();
try {
while (true) {
const { done, value } = await reader.read();
if (done) {
return;
}
// Forutsatt at API-et returnerer JSON-deler
const chunk = decoder.decode(value);
const items = JSON.parse(`[${chunk.replace(/\}\{/g, '},{')}]`); //Del opp deler til en gyldig json-array
for(const item of items){
yield item;
}
}
} finally {
reader.releaseLock();
}
}
async function processItem(item) {
// Simuler en asynkron operasjon
await new Promise(resolve => setTimeout(resolve, 100));
console.log(`Behandler element: ${JSON.stringify(item)}`);
}
async function main() {
const apiUrl = 'https://api.example.com/data'; // Erstatt med ditt API-endepunkt
await asyncForEach(fetchDataStream(apiUrl), processItem);
console.log('Ferdig med å behandle data.');
}
// Eksempel på bruk
main();
I dette eksempelet henter fetchDataStream data fra API-et og yielder hvert element etter hvert som det mottas. processItem-funksjonen simulerer en asynkron operasjon på hvert element. asyncForEach-hjelperen forenkler deretter iterasjons- og behandlingslogikken.
Forbedringer og Hensyn
Feilhåndtering
Det er avgjørende å håndtere feil som kan oppstå under asynkron iterasjon. Du kan pakke inn callback-funksjonen i en try...catch-blokk for å fange opp og håndtere unntak:
async function asyncForEach(iterable, callback) {
for await (const item of iterable) {
try {
await callback(item);
} catch (error) {
console.error(`Feil ved behandling av element: ${item}`, error);
// Du kan velge å kaste feilen på nytt eller fortsette behandlingen
}
}
}
Samtidighetskontroll
Som standard behandler den asynkrone 'for each'-hjelperen elementer sekvensielt. Hvis du trenger å behandle elementer samtidig, kan du bruke en Promise-pool for å begrense antall samtidige operasjoner:
async function asyncForEachConcurrent(iterable, callback, concurrency) {
const executing = [];
for await (const item of iterable) {
const p = callback(item).then(() => executing.splice(executing.indexOf(p), 1));
executing.push(p);
if (executing.length >= concurrency) {
await Promise.race(executing);
}
}
await Promise.all(executing);
}
async function processItem(item) {
// Simuler en asynkron operasjon
await new Promise(resolve => setTimeout(resolve, 100));
console.log(`Behandler element: ${JSON.stringify(item)}`);
}
async function main() {
const apiUrl = 'https://api.example.com/data'; // Erstatt med ditt API-endepunkt
await asyncForEachConcurrent(fetchDataStream(apiUrl), processItem, 5); // Samtidighet på 5
console.log('Ferdig med å behandle data.');
}
I dette eksempelet begrenser asyncForEachConcurrent antall samtidige callback-kjøringer til det angitte samtidighetsnivået. Dette kan forbedre ytelsen når man håndterer store datastrømmer.
Avbrytelse
I noen tilfeller kan det være nødvendig å avbryte iterasjonsprosessen for tidlig. Du kan oppnå dette ved å bruke en AbortController:
async function asyncForEach(iterable, callback, signal) {
for await (const item of iterable) {
if (signal && signal.aborted) {
console.log('Iterasjon avbrutt.');
return;
}
await callback(item);
}
}
async function main() {
const controller = new AbortController();
const signal = controller.signal;
setTimeout(() => {
controller.abort(); // Avbryt etter 2 sekunder
}, 2000);
const apiUrl = 'https://api.example.com/data'; // Erstatt med ditt API-endepunkt
await asyncForEach(fetchDataStream(apiUrl), processItem, signal);
console.log('Ferdig med å behandle data.');
}
I dette eksempelet sjekker asyncForEach-funksjonen signal.aborted-egenskapen før hver iterasjon. Hvis signalet er avbrutt, stoppes iterasjonen.
Virkelige Anvendelser
Asynkrone iteratorer og den asynkrone 'for each'-hjelperen kan brukes i et bredt spekter av virkelige scenarier:
- Databehandlings-pipelines: Behandling av store datasett fra databaser eller filsystemer.
- Sanntids datastrømmer: Håndtering av data fra web sockets, meldingskøer eller sensornettverk.
- API-konsumering: Henting og behandling av data fra API-er som returnerer strømmer av elementer.
- Bilde- og videobehandling: Behandling av store mediefiler i deler.
- Logganalyse: Analysere store loggfiler linje for linje.
Eksempel - Internasjonale Aksjedata: Tenk deg en applikasjon som henter sanntids aksjekurser fra ulike internasjonale børser. En asynkron iterator kan brukes til å strømme dataene, og en asynkron 'for each' kan behandle hver kurs, og oppdatere brukergrensesnittet med de siste prisene. Dette kan brukes til å vise gjeldende aksjekurser for selskaper som:
- Tencent (Kina): Hente aksjedata for et stort internasjonalt teknologiselskap
- Tata Consultancy Services (India): Vise aksjeoppdateringer fra et ledende IT-tjenesteselskap
- Samsung Electronics (Sør-Korea): Vise aksjekurser fra en global elektronikkprodusent
- Toyota Motor Corporation (Japan): Overvåke aksjekurser for en internasjonal bilprodusent
Konklusjon
Asynkrone iteratorer og den asynkrone 'for each'-hjelperen gir en kraftig og elegant måte å behandle datastrømmer asynkront i JavaScript. Ved å innkapsle iterasjonslogikken kan du forenkle koden din, forbedre lesbarheten og øke ytelsen til applikasjonene dine. Ved å håndtere feil, kontrollere samtidighet og muliggjøre avbrytelse, kan du skape robuste og skalerbare asynkrone databehandlings-pipelines.